﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

using Atosenet.IO;
using Atosenet.Protocol;


namespace Atosenet
{
    public class PipelinePool : Component
    {
        #region 成员

        public const int IO_CREATION_FAILED = -1;
        public const int DATA_LINK_CREATION_FAILED = -2;

        public event PacketsReceivedEventHandler PacketsReceived;
        public event LowLevelRawDataReceivedEventHandler RawDataReceived;
        public event LinkEstablishedEventHandler LinkEstablished;

        public event IOLinkNotFoundEventHandler IOLinkNotFound;
        public event IOLinkConnectFailedEventHandler IOLinkConnectFailed;
        public event DataIONotFoundEventHandler DataIONotFound;
        public event DataIOSendDataFailedEventHandler DataIOSendDataFailed;

        public IOManager _ioManager = new IOManager();
        public LinkManager _linkManager = new LinkManager();



        #endregion


        #region 初始化

        public PipelinePool()
        {
            Init();
        }

        private void Init()
        {
            _ioManager.Initialize(this);
            _linkManager.Initialize(this);

            _ioManager.IODataReceived += IOManager_IODataReceived;
            _ioManager.IOErrorOccured += IOManager_IOErrorOccured;
            _ioManager.IOManagementChanged += IOManager_IOManagementChanged;
            _linkManager.IODataGenerated += LinkManager_IODataGenerated;
            _linkManager.LinkStatusChanged += LinkManager_LinkStatusChanged;
            _linkManager.PacketReceived += LinkManager_PacketReceived;            
        }
                
        #endregion


        #region 公开函数

        public int AddIOPipe(IOParameter param, string protocolName)
        {
            int id = _ioManager.Create(param);

            if (id < 0)
                return IO_CREATION_FAILED;

            if (_linkManager.AddLink(id, protocolName))
                return id;
            else
                return DATA_LINK_CREATION_FAILED;
        }

        public bool RemoveIOPipe(int id)
        {
            if (id < 0)
            {
                for (int i = 0; i < _linkManager.DataLinks.Count; i++)
                {
                    RemoveIOPipeProc(_linkManager.DataLinks[i].LinkID);
                }
                return true;
            }

            return RemoveIOPipeProc(id);
        }
        
        public bool AddDataIO(IOParameter param)
        {
            return _ioManager.Create(param) > 0;
        }

        public bool RemoveDataIO(int id)
        {
            if (_ioManager.Destroy(id) <= 0)
                return !_ioManager.IOs.Exists(io => { return io.ID == id; });
            else
                return true;
        }

        public bool ConnectIOLink(int id, string protocolName)
        {
            if (!_ioManager.IOs.Exists(io => { return io.ID == id; }))
                return false;

            return _linkManager.AddLink(id, protocolName);
        }

        public bool DisconnectIOLink(int id)
        {
            return _linkManager.RemoveLink(id, id);
        }

        public bool StartIOPipe(int id)
        {
            if(_ioManager.IOs[id] == null)
                return false;

            if (!_ioManager.Open(id) && !_ioManager.IOs[id].IsOpened )
                return false;
            
            //if (_ioManager.IOs[id].IOMode == IOModeType.Server)
            //{
            //    return true;
            //}
            //else
            {
                return _linkManager.Start(id, id);
            }
        }

        public bool StopIOPipe(int id)
        {
            BaseIO io = _ioManager.IOs[id];

            if (io == null)
                return false;

            if (io.IOMode == IOModeType.Server)
            {
                for (int i = 0; i < io.Clients.Count; i++)
                    _linkManager.Stop(io.Clients[i].ID, io.ID);

                return _ioManager.Close(id);
            }
            else
            {
                return _linkManager.Stop(id, id) && _ioManager.Close(id);
            }
        }

        #endregion


        #region 静态函数



        #endregion


        #region 核心函数

        private bool RemoveIOPipeProc(int id)
        {
            IOLink link = _linkManager.DataLinks.Find((l) => { return l.LinkID == id; });

            if (link != null)
            {
                BaseIO io = _ioManager.IOs[link.HostID];

                if (io != null)
                {
                    if (io.IOMode == IOModeType.Server && !link.IsClientLink)       /* 这边的逻辑有漏洞！ */
                    {
                        for (int i = 0; i < io.Clients.Count; i++)
                            _linkManager.RemoveLink(io.Clients[i].ID, io.ID);

                        _ioManager.Destroy(id);
                        return true;
                    }
                    else
                    {
                        return _linkManager.RemoveLink(link.LinkID, link.HostID) && _ioManager.Destroy(id) > 0;
                    }
                }
                else
                {
                    return _linkManager.RemoveLink(link.LinkID, link.HostID);
                }
            }
            else
            {
                BaseIO io = _ioManager.IOs[id];

                if (io == null)
                    return false;

                return _ioManager.Destroy(id) > 0;
            }           
        }

        private void IOManager_IOManagementChanged(object sender, IOManagementEventArgs e)
        {
            switch (e.EventType)
            {
                case IOManagementType.Created:

                    break;
                case IOManagementType.Destroyed:

                    break;
                case IOManagementType.Opened:
                    BaseIO io = _ioManager.IOs[e.HostID];

                    if (io != null && io.IOMode == IOModeType.Server)
                    {
                        BaseIO.ClientIO client = io.Clients.Find(e.ID);

                        if (client != null)
                        {
                            OnLinkEstablished(e.ID, client.Address.Address.ToString(), client.Address.Port);
                        }
                    }

                    break;
                case IOManagementType.Closed:
                    
                    break;
                case IOManagementType.ReceivingData:

                    break;

                case IOManagementType.ErrorOccured:
                    //RemoveDataIO(e.ID);
                    RemoveIOPipe(e.ID);
                    OnIOLinkConnectFailed(e.ID, e.InfoCode);
                    break;

                case IOManagementType.ClientIOAdded:
                    LinkToClientIO(e.ID, e.HostID);
                    break;

                case IOManagementType.ClientIORemoved:
                    UnlinkToClientIO(e.ID, e.HostID);
                    break;

                default:
                    break;
            }
        }

        private void LinkToClientIO(int clientId, int hostId)
        {
            if (!_linkManager.DataLinks.Exists(link => { return link.LinkID == hostId; }))
            {
                OnIOLinkNotFound(hostId);
                return;
            }
            string pName = _linkManager.DataLinks.Find(link => { return link.LinkID == hostId; }).ProtocolType;

            if (_linkManager.AddLink(clientId, hostId, pName))
            {
                _linkManager.Start(clientId, hostId);                
            }
        }

        private void UnlinkToClientIO(int clientId, int hostId)
        {
            _linkManager.RemoveLink(clientId, hostId);
        }

        private void IOManager_IOErrorOccured(object sender, IODispatcherErrorOccuredEventArgs e)
        {

        }

        private void IOManager_IODataReceived(object sender, IODataReceivedEventArgs e)
        {
            OnRawDataReceived(e.ID, e.HostID, e.Data, e.Timestamp);

            if (!_linkManager.ReceiveFrom(e.ID, e.HostID, e.Data))
            {
                if (e.IsClientMode)
                {
                    if (!_linkManager.DataLinks.Exists(link => { return link.LinkID == e.HostID; }))
                    {
                        OnIOLinkNotFound(e.ID);
                        return;
                    }
                    //string pName = _linkManager.DataLinks.Find(link => { return link.LinkID == e.HostID; }).ProtocolType;

                    //if (_linkManager.AddLink(e.ID, e.HostID, pName))
                    //{
                    //    _linkManager.Start(e.ID, e.HostID);
                    //    _linkManager.ReceiveFrom(e.ID, e.HostID, e.Data);
                    //}
                }
                else
                {
                    OnIOLinkNotFound(e.ID);
                }
            }
        }

        private void LinkManager_LinkStatusChanged(object sender, LinkStatusChangedEventArgs e)
        {

        }

        private void LinkManager_PacketReceived(object sender, IOLinkPacketReceivedEventArgs e)
        {
            if (e.Packets != null && e.Packets.Length > 0)
            {
                OnPacketsReceived(e.LinkID, e.HostID, e.Packets, e.Timestamp);
            }
        }

        private void LinkManager_IODataGenerated(object sender, IODataGeneratedEventArgs e)
        {
            if (!_ioManager.IOs.Exists(io => { return io.ID == e.HostID; }))
            {
                OnDataIONotFound(e.HostID);
                return;
            }

            bool result = false;

            if (e.IsClient)
            {
                result = _ioManager.SendToIO(e.LinkID, e.HostID, e.Data);
            }
            else
            {
                result = _ioManager.SendToIO(e.LinkID, e.Data);
            }

            if (!result)
                OnDataIOInvertedOutputFailed(e.HostID);
        }

        #endregion


        #region 属性



        #endregion


        #region 事件

        private void OnIOLinkConnectFailed(int id, int errorCode)
        {
            if (IOLinkConnectFailed != null)
            {
                IOLinkConnectFailedEventArgs e = new IOLinkConnectFailedEventArgs(id, errorCode);
                IOLinkConnectFailed(this, e);
            }
        }

        private void OnIOLinkNotFound(int id)
        {
            if (IOLinkNotFound != null)
            {
                IOLinkNotFoundEventArgs e = new IOLinkNotFoundEventArgs(id);
                IOLinkNotFound(this, e);
            }
        }

        private void OnDataIONotFound(int id)
        {
            if (DataIONotFound != null)
            {
                DataIONotFoundEventArgs e = new DataIONotFoundEventArgs(id);
                DataIONotFound(this, e);
            }
        }

        private void OnDataIOInvertedOutputFailed(int id)
        {
            if (DataIOSendDataFailed != null)
            {
                DataIOSendDataFailedEventArgs e = new DataIOSendDataFailedEventArgs(id);
                DataIOSendDataFailed(this, e);
            }
        }

        private void OnPacketsReceived(int linkId, int hostId, IProtocolPacket[] packets, DateTime timestamp)
        {
            if (PacketsReceived != null)
            {
                PacketsReceivedEventArgs e = new PacketsReceivedEventArgs(linkId, hostId, packets, timestamp);
                PacketsReceived(this, e);
            }
        }

        private void OnRawDataReceived(int linkId, int hostId, byte[] data, DateTime time)
        {
            if (RawDataReceived != null)
            {
                LowLevelRawDataReceivedEventArgs e = new LowLevelRawDataReceivedEventArgs(linkId, hostId, data, time);
                RawDataReceived(this, e);
            }
        }

        private void OnLinkEstablished(int linkId, string ip, int port)
        {
            if (LinkEstablished != null)
            {
                LinkEstablishedEventArgs e = new LinkEstablishedEventArgs(linkId, ip, port);
                LinkEstablished(this, e);
            }
        }

        #endregion
    }



    public delegate void LinkEstablishedEventHandler(object sender, LinkEstablishedEventArgs e);

    public class LinkEstablishedEventArgs : EventArgs
    {
        public int LinkID = 0;
        public string IP = string.Empty;
        public int Port = 0;

        public LinkEstablishedEventArgs(int linkId, string ip, int port)
        {
            LinkID = linkId;
            IP = ip;
            Port = port;
        }
    }

    public delegate void LowLevelRawDataReceivedEventHandler(object sender, LowLevelRawDataReceivedEventArgs e);

    public class LowLevelRawDataReceivedEventArgs : EventArgs
    {
        public DateTime Timestamp;
        public int LinkID;
        public int HostID;
        public byte[] Data;

        public LowLevelRawDataReceivedEventArgs(int linkId, int hostId, byte[] data, DateTime time)
        {
            HostID = hostId;
            LinkID = linkId;
            Timestamp = time;
            Data = data;
        }
    }

    public class CommuInfoEventArgs : EventArgs
    {
        public int ID;
        public DateTime Timestamp;

        public CommuInfoEventArgs(int id)
        {
            ID = id;
            Timestamp = DateTime.Now;
        }
    }
        
    public delegate void IOLinkNotFoundEventHandler(object sender, IOLinkNotFoundEventArgs e);

    public class IOLinkNotFoundEventArgs : CommuInfoEventArgs
    {
        public IOLinkNotFoundEventArgs(int id)
            : base(id)
        { }
    }

    public delegate void IOLinkConnectFailedEventHandler(object sender, IOLinkConnectFailedEventArgs e);

    public class IOLinkConnectFailedEventArgs : CommuInfoEventArgs
    {
        public int ErrorCode;

        public IOLinkConnectFailedEventArgs(int id, int errorCode)
            : base(id)
        {
            ErrorCode = errorCode;
        }
    }

    public delegate void DataIONotFoundEventHandler(object sender, DataIONotFoundEventArgs e);

    public class DataIONotFoundEventArgs : CommuInfoEventArgs
    {
        public DataIONotFoundEventArgs(int id)
            : base(id)
        { }
    }

    public delegate void DataIOSendDataFailedEventHandler(object sender, DataIOSendDataFailedEventArgs e);

    public class DataIOSendDataFailedEventArgs : CommuInfoEventArgs
    {
        public DataIOSendDataFailedEventArgs(int id)
            : base(id)
        { }
    }

    public delegate void PacketsReceivedEventHandler(object sender, PacketsReceivedEventArgs e);

    public class PacketsReceivedEventArgs : IOLinkPacketReceivedEventArgs
    {
        public PacketsReceivedEventArgs(int linkId, int hostId, IProtocolPacket[] packets, DateTime timestamp)
            : base(linkId, hostId, packets, timestamp)
        { }
    }
}
